<?php
/**
 * FastApp.
 * 1/24/25
 * @Author deepsea
 * @Contact (telegram:deepsea159)
 */

namespace App\Fastapp\Api\Service\Crypto\Coin;

use App\Fastapp\Api\Service\Crypto\Config;
use App\Fastapp\Model\AccountWalletAddress;
use Brick\Math\BigInteger;
use Http\Client\Common\HttpMethodsClient;
use Http\Discovery\Psr17FactoryDiscovery;
use Http\Discovery\Psr18ClientDiscovery;
use Olifanton\Interop\Address;
use Olifanton\Interop\Bytes;
use Olifanton\Interop\Helpers\OlifantonByteReader;
use Olifanton\Interop\KeyPair;
use Olifanton\Mnemonic\TonMnemonic;
use Olifanton\Ton\Contracts\Wallets\V3\WalletV3Options;
use Olifanton\Ton\Contracts\Wallets\V3\WalletV3R2;
use Olifanton\TypedArrays\Uint8Array;
use Olifanton\Ton\Transports\Toncenter\ClientOptions;
use Olifanton\Ton\Transports\Toncenter\ToncenterHttpV2Client;
use Olifanton\Ton\Transports\Toncenter\ToncenterTransport;
use Olifanton\Interop\Units;
use Olifanton\Ton\Contracts\Jetton\JettonMinter;
use Olifanton\Ton\Contracts\Jetton\JettonWallet;
use Olifanton\Ton\Contracts\Jetton\JettonWalletOptions;
use Olifanton\Ton\Contracts\Jetton\TransferJettonOptions;
use Olifanton\Ton\Contracts\Wallets\Transfer;
use Olifanton\Ton\Contracts\Wallets\TransferOptions;
use Olifanton\Ton\SendMode;

class Ton
{
    public static function genAddress(): array
    {
        $mnemonic = TonMnemonic::generate();
        $keyPair = TonMnemonic::mnemonicToKeyPair($mnemonic);
        $wallet = self::getWalletV3R2($keyPair->publicKey);
        $address = $wallet->getAddress()->toString(true, true, false);
        $base64 = base64_encode(OlifantonByteReader::getBytes($keyPair->secretKey->buffer));
        return [$address, rtrim(strtr($base64, '+/', '-_'), '='), implode(' ', $mnemonic)];
    }

    public static function privateKeyFormat(string $privateKey): KeyPair
    {
        $privateKeyBytes = Bytes::base64ToBytes(strtr($privateKey, '-_', '+/'));
        return KeyPair::fromSecretKey($privateKeyBytes);
    }

    public static function addressFormat(string $address, bool $isUserFriendly = true, bool $isBounceable = false, bool $isUrlSafe = true): string
    {
        $address = new Address($address);
        return $address->toString($isUserFriendly, $isUrlSafe, $isBounceable, false);
    }

    public static function getWalletV3R2(Uint8Array $publicKey): WalletV3R2
    {
        return new WalletV3R2(new WalletV3Options($publicKey));
    }

    public static function toncenterTransport(): ToncenterTransport
    {
        return new ToncenterTransport(new ToncenterHttpV2Client(
            new HttpMethodsClient(
                Psr18ClientDiscovery::find(),
                Psr17FactoryDiscovery::findRequestFactory(),
                Psr17FactoryDiscovery::findStreamFactory(),
            ),
            new ClientOptions(
                ClientOptions::MAIN_BASE_URL,
                Config::TON_API_KEY,
            ),
        ));
    }

    public static function transTon(AccountWalletAddress $fromAddress, string $toAddress, string $amount): int|bool
    {
        $transport = self::toncenterTransport();
        $keyPair = self::privateKeyFormat($fromAddress->private_key);
        $toAddress = new Address($toAddress);
        $amount = Units::toNano($amount);

        $wallet = self::getWalletV3R2($keyPair->publicKey);
        $nonce = (int)$wallet->seqno($transport);
        if ($nonce && (int)$fromAddress->nonce >= $nonce) return false;
        $transfer = new Transfer(dest: $toAddress, amount: $amount);
        $extMsg = $wallet->createTransferMessage([$transfer], new TransferOptions($nonce));
        $feeNano = $transport->estimateFee($wallet->getAddress(), $extMsg->cell());
        $totalCost = BigInteger::of($amount)->plus(Units::toNano($feeNano->toFloat()));
        $transfer = new Transfer(dest: $toAddress, amount: $totalCost);
        $extMsg = $wallet->createTransferMessage([$transfer], new TransferOptions($nonce));
        $transport->sendMessage($extMsg, $keyPair->secretKey);
        $fromAddress->nonce = $nonce;
        $fromAddress->save();
        return $nonce;
    }

    public static function transTonUsdt(AccountWalletAddress $fromAddress, string $toAddress, string $amount): bool
    {
        $transport = self::toncenterTransport();
        $keyPair = self::privateKeyFormat($fromAddress->private_key);
//        $textComment = SnakeString::fromString('"(૭ ｡•̀ ᵕ •́｡ )૭"')->cell(true);
        $textComment = null;
        $wallet = self::getWalletV3R2($keyPair->publicKey);
        $walletAddress = $wallet->getAddress();
        $usdtRoot = JettonMinter::fromAddress(
            $transport,
            new Address(Config::TON_CONTRACT_ADDRESS_USDT),
        );
        $usdtWalletAddress = $usdtRoot->getJettonWalletAddress($transport, $walletAddress);
        $usdtWallet = new JettonWallet(new JettonWalletOptions(
            address: $usdtWalletAddress,
        ));

        $nonce = (int)$wallet->seqno($transport);
        if ((int)$fromAddress->nonce >= $nonce) return false;
        $extMessage = $wallet->createTransferMessage([
            new Transfer(
                dest: $usdtWalletAddress,
                amount: Units::toNano($amount, Units::USDt),
                payload: $usdtWallet->createTransferBody(
                    new TransferJettonOptions(
                        jettonAmount: Units::toNano($amount, Units::USDt),
                        toAddress: new Address($toAddress),
                        responseAddress: $walletAddress,
                        forwardPayload: $textComment,
                        forwardAmount: Units::toNano("0.0000001"),
                    ),
                ),
                sendMode: SendMode::IGNORE_ERRORS->combine(SendMode::PAY_GAS_SEPARATELY, SendMode::CARRY_ALL_REMAINING_INCOMING_VALUE),
            )
        ], new TransferOptions(
            seqno: $nonce,
        ));
        $transport->sendMessage($extMessage, $keyPair->secretKey);
        $fromAddress->nonce = $nonce;
        $fromAddress->save();
        return true;
    }
}